package exp;

import utils.Buffer;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

public class Bmp
{
    private int offset, width, height, bitCnt;
    private int biSize, biPlanes, biCompression, biSizeImage, biXPelsPerMeter, biYPelsPerMeter, biClrUsed, biClrImportant;
    private String        probeContentType;
    private BufferedImage image;

    public Bmp()
    {
        offset=width=height=bitCnt=0;
        biSize=biPlanes=biCompression=biSizeImage=biXPelsPerMeter=biYPelsPerMeter=biClrUsed=biClrImportant=0;
        image =null;
    }

    public Bmp(Path path) throws IOException, BmpStructureErrorException
    {
        this();
        readBmp(path);
    }

    public BufferedImage getImage()
    {
        return image;
    }

    public int getOffset()
    {
        return offset;
    }

    public int getWidth()
    {
        return width;
    }

    public int getHeight()
    {
        return height;
    }

    public int getBitCnt()
    {
        return bitCnt;
    }

    public int getBiSize()
    {
        return biSize;
    }

    public int getBiPlanes()
    {
        return biPlanes;
    }

    public int getBiCompression()
    {
        return biCompression;
    }

    public int getBiSizeImage()
    {
        return biSizeImage;
    }

    public int getBiXPelsPerMeter()
    {
        return biXPelsPerMeter;
    }

    public int getBiYPelsPerMeter()
    {
        return biYPelsPerMeter;
    }

    public int getBiClrUsed()
    {
        return biClrUsed;
    }

    public int getBiClrImportant()
    {
        return biClrImportant;
    }

    public String getProbeContentType()
    {
        return probeContentType;
    }

    void readBmp(Path filePath) throws IOException, BmpStructureErrorException
    {
        if(!"image/bmp".equals(probeContentType=Files.probeContentType(filePath)))
        {
            image=ImageIO.read(filePath.toFile());
        }
        else
        {
            byte[] buf=Files.readAllBytes(filePath);
            if('B'!=Buffer.getUint8(buf,0))
                throw new BmpStructureErrorException();
            if('M'!=Buffer.getUint8(buf,1))
                throw new BmpStructureErrorException();
            if(buf.length!=Buffer.getUint32(buf,2))
                throw new BmpStructureErrorException();
            if(0!=Buffer.getUint32(buf,30))         //compresion
                throw new BmpStructureErrorException();

            offset=(int)Buffer.getUint32(buf,10);
            width =(int)Buffer.getUint32(buf,18);
            height=(int)Buffer.getUint32(buf,22);
            bitCnt=Buffer.getUint16(buf,28);

            biSize         =(int)Buffer.getUint32(buf,14);
            biPlanes       =(int)Buffer.getUint16(buf,26);
            biCompression  =(int)Buffer.getUint32(buf,30);
            biSizeImage    =(int)Buffer.getUint32(buf,34);
            biXPelsPerMeter=(int)Buffer.getUint32(buf,38);
            biYPelsPerMeter=(int)Buffer.getUint32(buf,42);
            biClrUsed      =(int)Buffer.getUint32(buf,46);
            biClrImportant =(int)Buffer.getUint32(buf,50);

            final int rs=((((width)*bitCnt)+31)>>5)<<2;

            if(!((offset==54&&bitCnt==24)||(offset==4*256+54&&bitCnt==8)||(offset==4*16+54&&bitCnt==4)||(offset==4*2+54&&bitCnt==1)))
                throw new BmpStructureErrorException();
            if(!((rs*height+offset)==buf.length))
                throw new BmpStructureErrorException();

            image=new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
            if(bitCnt==24)
            {
                for(int y=0;y<height;++y)
                {
                    final int nn=(height-1-y)*rs;
                    for(int x=0;x<width;++x)
                    {
                        final int n=nn+x*3;
                        image.setRGB(x,y,(Buffer.getUint8(buf,offset+n+2)<<16)|(Buffer.getUint8(buf,offset+n+1)<<8)|Buffer.getUint8(buf,offset+n));
                    }
                }
            }
            else if(bitCnt==8)
            {
                for(int y=0;y<height;++y)
                {
                    final int nn=(height-1-y)*rs;
                    for(int x=0;x<width;++x)
                    {
                        final int n=nn+x;
                        final int t=Buffer.getUint8(buf,offset+n);
                        image.setRGB(x,y,(Buffer.getUint8(buf,54+t*4+2)<<16)|(Buffer.getUint8(buf,54+t*4+1)<<8)|Buffer.getUint8(buf,54+t*4));
                    }
                }
            }
            else if(bitCnt==4)
            {
                for(int y=0;y<height;++y)
                {
                    final int nn=(height-1-y)*rs;
                    for(int x=0;x<width;++x)
                    {
                        final int n=nn+x/2;
                        final int t=(Buffer.getUint8(buf,offset+n)>>((1-(x&1))*4))&0XF;
                        image.setRGB(x,y,(Buffer.getUint8(buf,54+t*4+2)<<16)|(Buffer.getUint8(buf,54+t*4+1)<<8)|Buffer.getUint8(buf,54+t*4));
                    }
                }
            }
            else if(bitCnt==1)
            {
                for(int y=0;y<height;++y)
                {
                    final int nn=(height-1-y)*rs;
                    for(int x=0;x<width;++x)
                    {
                        final int n=nn+x/8;
                        final int t=(Buffer.getUint8(buf,offset+n)>>(7-(x&7)))&0X1;
                        image.setRGB(x,y,(Buffer.getUint8(buf,54+t*4+2)<<16)|(Buffer.getUint8(buf,54+t*4+1)<<8)|Buffer.getUint8(buf,54+t*4));
                    }
                }
            }
        }
    }
}
